using System;
using System.Collections.Generic;
using System.Text;

namespace GPSNav.NET
{
	public class GpsMap
	{
		private IGpsDataAccess access;
		private string mapName;
		public GpsMap(string mapName)
			: this(mapName, IAccessType.FileSystem)
		{

		}
		public GpsMap(string mapName, IAccessType accessType)
		{
			this.mapName = mapName;
			Initialize(accessType);
		}
		private void Initialize(IAccessType accessType)
		{
			switch (accessType)
			{
				case IAccessType.FileSystem:
					throw new NotImplementedException("FileSystem not supported");
					//access = new FSAccess(this.mapName);
					break;
				case IAccessType.SQLite:
					//G:\gps\gps.sqlite

					//Console.WriteLine(GPSNav.NET.Properties.Settings.Default.DbFileName);
					access = new SQLiteGpsDataAccess(GPSNav.NET.Properties.Settings.Default.DbFileName);


					break;
				case IAccessType.PgSql:
					throw new NotImplementedException("PgSql not supported");
					break;
			}
			bounds = access.GetBounds();
		}


		private GeoRectangle bounds;
		public GeoRectangle Bounds
		{
			get { return bounds; }
		}





		/*private double dist(GpsNode p1, GpsNode p2)
		{
			if (p1 == null)
				Console.WriteLine("p1 NULL");
			if (p2 == null)
				Console.WriteLine("p2 NULL");
			if (p2 == null || p2 == null)
				return double.PositiveInfinity;

			try
			{

				//return Math.Pow(p1.Lat - p2.Lat, 2) + Math.Pow(p1.Lon - p2.Lon, 2);
				return Math.Pow(p1.Location.Lat - p2.Location.Lat, 2) + Math.Pow(p1.Location.Lon - p2.Location.Lon, 2);
			}
			catch (Exception ex)
			{
				Console.WriteLine(ex.ToString());
				Console.WriteLine("p1,p2: {0},{1}",p1,p2);
				return double.PositiveInfinity;
			}
			//return Math.Pow(p1.X - p2.X, 2) + Math.Pow(p1.Y - p2.Y, 2);
			//return 0;
		}
			 */
		public GpsRoute SearchRoute(Int64 startNodeId, Int64 endNodeId)
		{
			Console.WriteLine("SearchRoute({0},{1})", startNodeId, endNodeId);
			Console.WriteLine("NODE {0}", access.GetNode(startNodeId));
			GpsNodesCollection nn = access.GetNeighbors(startNodeId);

			if (nn != null)
				Console.WriteLine("NODE Neighbors {0}", nn.Count);



			GpsNodesCollection closedSet = new GpsNodesCollection();
			GpsNodesCollection openSet = new GpsNodesCollection();
			Dictionary<GpsNode, double> g_scores = new Dictionary<GpsNode, double>();
			Dictionary<GpsNode, double> f_scores = new Dictionary<GpsNode, double>();
			Dictionary<GpsNode, double> h_scores = new Dictionary<GpsNode, double>();
			Dictionary<GpsNode, GpsNode> came_from = new Dictionary<GpsNode, GpsNode>();

			GpsNode start = access.GetNode(startNodeId);
			GpsNode target = access.GetNode(endNodeId);

			openSet.Add(start);
			g_scores [start] = 0;

			while (openSet.Count > 0)
			{
				GpsNode xNode = null;
				double fscore = Double.PositiveInfinity;
				foreach (GpsNode nNode in openSet)
				{
					if (!f_scores.ContainsKey(nNode))
					{
						f_scores.Add(nNode, 0);
					}
					if (fscore > f_scores [nNode])
					{
						fscore = Math.Min(f_scores [nNode], fscore);
						xNode = nNode;
					}
				}
				if (xNode == target)
				{
					GpsNode tmp = target;
					GpsRoute llist = new GpsRoute();
					llist.AddFirst(tmp);
					while (came_from.ContainsKey(tmp))
					{

						if (came_from [tmp] != null)
						{
							llist.AddFirst(came_from [tmp]);
							//llist.AddLast(GetEdge(came_from [tmp], tmp, true));
						}
						tmp = came_from [tmp];
					}
					//GC.Collect(2, GCCollectionMode.Optimized);
					return llist;
					//return ;
				}

				openSet.Remove(xNode);
				closedSet.Add(xNode);

				GpsNodesCollection neighbors_x = access.GetNeighbors(xNode.NodeId);
				foreach (GpsNode yNode in neighbors_x)
				{
					if (closedSet.Contains(yNode))
					{
						continue;
					}
					//double tg_score = g_scores [xNode] + dist(xNode, yNode);
					double tg_score = g_scores [xNode] + (xNode ^ yNode);
					bool t_isbetter = false;
					if (!openSet.Contains(yNode))
					{
						openSet.Add(yNode);
						//h_scores [yNode] = dist(yNode, target);// := heuristic_estimate_of_distance_to_goal_from(yNode)
						h_scores [yNode] = (yNode ^ target);// := heuristic_estimate_of_distance_to_goal_from(yNode)
						t_isbetter = true;
					}
					else if (tg_score < g_scores [yNode])
					{
						t_isbetter = true;
					}
					if (t_isbetter)
					{
						came_from [yNode] = xNode;
						g_scores [yNode] = tg_score;
						f_scores [yNode] = tg_score + h_scores [yNode];
					}
				}
			}



			//GC.Collect(2, GCCollectionMode.Optimized);
			return null;
		}

		public GpsNode GetNode(Int64 nodeId)
		{
			return access.GetNode(nodeId);
		}

		public GpsNodesCollection GetNodes(GeoRectangle rect)
		{
			return access.GetNodes(rect);
		}

		public GpsWaysCollection GetWays(GeoRectangle rect, int lod)
		{
			return access.GetWays(rect, lod);
		}
		public GpsWaysCollection GetWays(GeoRectangle rect, int lod, string lodOperator)
		{
			return access.GetWays(rect, lod, lodOperator);
		}

		public GpsWaysCollection GetWays(GeoRectangle rect)
		{
			return access.GetWays(rect);
		}




	}
}
